package cmd

import (
	"fmt"
	"log"
	"path/filepath"
	"time"

	"github.com/spf13/cobra"
	"io.plus/test_loss/pkg/tl_client"
	"io.plus/test_loss/pkg/tl_client/tl_check"
	"io.plus/test_loss/pkg/tl_serv"
)

// 定义一个变量存储 Git Hash
var GitHash string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "",
	Short: "a tool for test data loss",
	Long: `
	include three subcommands: serv, cli, clicheck

	serv:
		run on a server, save client max count, can not stop, before client check it data.

	cli: 
		run on a client, send count to server, when it is running, 
		tester need force power off this node real machine, to test it loss data.

	clicheck:
		run which cli client, after machine boot from a "force power off", to check which file is not correct.
	`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf(`
		# use as bin
		wget "http://mirror.wh.io.plus/test_loss/test_loss_$(uname -m)"  -O test_loss
		chmod +x test_loss
		./test_loss


		# install as service (just for server)
		# update same as install
		wget -qO- \
			http://mirror.wh.io.plus/test_loss/deploy/systemd/install_test_loss_as_service.sh \
			| bash
		
		systemctl status test-loss.service
		output:
			● test-loss.service - check ips is or not mount service
				Loaded: loaded (/lib/systemd/system/test-loss.service; enabled; vendor preset: enabled)
				Active: active (running) since Thu 2024-11-21 08:01:05 EST; 14s ago


		#server
		./test_loss serv --addr=0.0.0.0:51234
		ouput: 
			2024/11/20 20:38:32 parse addr is 0.0.0.0:51234


		# client
		./test_loss cli --target=<server_ip>:51234  --job_id=<job_name>
		./test_loss cli --target=10.0.40.11:51234  --job_id=123
		ouput: 
			2024/11/21 09:57:42 write file success, file , content: /root/test_loss_record/123/4333, 4333
			2024/11/21 09:57:42 Time is up, signaling goroutine to exit, dura is 1 s
			2024/11/21 09:57:42 wait for stream closeing...
			2024/11/21 09:57:42 hash md5Bytes: da6cb383f8f9e58f2c8af88a8c0eb65e
			2024/11/21 09:57:42 Received count: 4334
			2024/11/21 09:57:42 write file success, file , content: /root/test_loss_record/123/4334, 4334
			2024/11/21 09:57:42 stream closed normally


		# client check
		./test_loss clicheck --target=<server_ip>:51234 --job_id=<job_name>
		./test_loss clicheck --target=10.0.40.11:51234  --job_id=123
		ouput: 
			2024/11/21 09:57:52 jobid:  123
			2024/11/21 09:57:52 max count: 4334
			2024/11/21 09:57:52 checkJobRes: {"Sum":4334,"Uncorrect":0,"Loss":0}

`)
	},
}

// Execute adds all child commands to the root command and sets flags appropriately.
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
	}
}

// versionCmd
var versionCmd = &cobra.Command{
	Use:     "version",
	Aliases: []string{"v", "ver"},
	Short:   "print version",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("git %s\n", GitHash)
	},
}

// servcmdCmd represents the servcmd command
var servCmd = &cobra.Command{
	Use:   "serv",
	Short: "serv can not be power off",
	Long:  `run on a server, save client max count, can not stop, before client check it data.`,
	Run: func(cmd *cobra.Command, args []string) {
		addr, _ := cmd.Flags().GetString("addr")
		log.Printf("parse addr is %s\n", addr)
		tl_serv.Run(addr)
	},
}

// clicmdCmd represents the clicmd command
var cliCmd = &cobra.Command{
	Use:   "cli",
	Short: "cli",
	Long:  `cli for run test`,
	Run: func(cmd *cobra.Command, args []string) {
		target, err := cmd.Flags().GetString("target")
		if err != nil {
			log.Fatalf("parse target err: %v\n", err)
		}
		log.Printf("parse target is %s\n", target)
		duration, err := cmd.Flags().GetInt("duration")
		if err != nil {
			log.Fatalf("parse duration err: %v\n", err)
		}
		log.Printf("parse duration is %v\n", duration)
		// parse job dir
		job_dir, err := cmd.Flags().GetString("job_dir")
		if err != nil {
			log.Fatalf("parse job_dir err: %v\n", err)
		}
		log.Printf("parse job_dir is %s\n", job_dir)

		// parse DSYNC
		is_dsync, err := cmd.Flags().GetBool("dsync")
		if err != nil {
			log.Fatalf("parse is_dsync err: %v\n", err)
		}
		log.Printf("parse is_dsync is %v\n", is_dsync)

		// fsync
		is_fsync, err := cmd.Flags().GetBool("fsync")
		if err != nil {
			log.Fatalf("parse is_dsync err: %v\n", err)
		}
		log.Printf("parse is_fsync is %v\n", is_fsync)

		// 检查 job_dir 是否是 绝对目录
		if !filepath.IsAbs(job_dir) {
			log.Fatalf("job_dir is not an absolute path: %s", job_dir)
		}

		// job_id
		job_id, err := cmd.Flags().GetString("job_id")
		if err != nil {
			log.Fatalf("parse job_id err: %v\n", err)
		}

		use_db, err := cmd.Flags().GetBool("db")
		if err != nil {
			log.Fatalf("parse use_db err: %v\n", err)
		}

		//
		job_full_path := filepath.Join(job_dir, job_id)
		log.Printf("parse job_full_path is %s\n", job_full_path)

		tl_client.GlbClientArgs = tl_client.ClientArgs{
			Target:      target,
			Duration:    duration,
			JobDir:      job_dir,
			JobID:       job_id,
			JobFullPath: job_full_path,
			IsDsync:     is_dsync,
			IsFsync:     is_fsync,
			UseDB:       use_db,
		}

		log.Printf("parse job_id is %s, args is %v\n", job_id, tl_client.GlbClientArgs)
		tl_client.Run()
	},
}

var clicheckCmd = &cobra.Command{
	Use:   "clicheck",
	Short: "clicheck",
	Long:  `Check if the client has lost data.`,
	Run: func(cmd *cobra.Command, args []string) {
		target, err := cmd.Flags().GetString("target")
		if err != nil {
			log.Fatalf("parse target err: %v\n", err)
		}
		log.Printf("parse target is %s\n", target)
		// parse job dir
		job_dir, err := cmd.Flags().GetString("job_dir")
		if err != nil {
			log.Fatalf("parse job_dir err: %v\n", err)
		}
		log.Printf("parse job_dir is %s\n", job_dir)

		// 检查 job_dir 是否是 绝对目录
		if !filepath.IsAbs(job_dir) {
			log.Fatalf("job_dir is not an absolute path: %s", job_dir)
		}

		// job_id
		job_id, err := cmd.Flags().GetString("job_id")
		if err != nil {
			log.Fatalf("parse job_id err: %v\n", err)
		}

		//
		use_db, err := cmd.Flags().GetBool("db")
		if err != nil {
			log.Fatalf("parse use_db err: %v\n", err)
		}

		//
		job_full_path := filepath.Join(job_dir, job_id)
		log.Printf("parse job_full_path is %s\n", job_full_path)

		log.Printf("parse job_id is %s\n", job_id)

		tl_check.GlbClientCheckArgs = tl_check.ClientCheckArgs{Target: target, JobDir: job_dir, JobID: job_id, JobFullPath: job_full_path, UseDB: use_db}
		tl_check.Run()
	},
}

func init() {
	// Here we define the flags for the root command.
	// Cobra supports persistent flags which, if defined here,
	// will be global for your application.
	servCmd.PersistentFlags().StringP("addr", "a", "0.0.0.0:50051", "listen addr")
	rootCmd.AddCommand(servCmd)

	// 获取当前时间
	now := time.Now()
	// 定义时间格式
	const layout = "20060102_15_04_05"
	// 格式化时间字符串
	currentTimeStr := now.Format(layout)
	{
		cliCmd.PersistentFlags().StringP("target", "t", "127.0.0.1:50051", "client conn target")
		cliCmd.PersistentFlags().IntP("duration", "d", 600, "second of request duration, 0 mean run forever")

		// 定义job目录
		cliCmd.PersistentFlags().StringP("job_dir", "", "/root/test_loss_record", "job base workdir")
		cliCmd.PersistentFlags().StringP("job_id", "", currentTimeStr, "job id default is current time")

		cliCmd.PersistentFlags().BoolP("dsync", "", true, "set write type = dsync, default is true, --dsync=0 set false")
		cliCmd.PersistentFlags().BoolP("fsync", "", true, "set write type = fsync, default is true, --fsync=0 set false")

		// 直接在 client 使用 sqlite3 存储计数，模拟真实场景下数据库数据是否减少
		cliCmd.PersistentFlags().BoolP("db", "", true, "use sqlite db to save count, --db=0 set false to use file save count")

		rootCmd.AddCommand(cliCmd)
	}
	//
	{
		clicheckCmd.PersistentFlags().StringP("target", "t", "127.0.0.1:50051", "client conn target")
		// 定义job目录
		clicheckCmd.PersistentFlags().StringP("job_dir", "", "/root/test_loss_record", "job base workdir")
		clicheckCmd.PersistentFlags().StringP("job_id", "", currentTimeStr, "job id default is current time")

		// 直接在 client 使用 sqlite3 存储计数，模拟真实场景下数据库数据是否减少
		clicheckCmd.PersistentFlags().BoolP("db", "", true, "use sqlite db to save count, --db=0 set false to use file save count")

		rootCmd.AddCommand(clicheckCmd)
	}
	//
	{
		rootCmd.AddCommand(versionCmd)
	}
}
